package ms.jasim.console.gui.component;

import java.awt.BorderLayout;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.util.ArrayList;

import javax.swing.DefaultCellEditor;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JTable;
import javax.swing.KeyStroke;
import javax.swing.ListSelectionModel;
import javax.swing.border.TitledBorder;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableCellEditor;
import javax.swing.table.TableColumn;

import ms.jasim.framework.NamedListComboBoxModel;
import ms.jasim.framework.PddlModel;
import ms.jasim.framework.PddlModel.Actor;
import ms.jasim.framework.PddlModel.Capacity;
import ms.jasim.framework.PddlModel.Goal;
import ms.spm.IAppContext;
import ms.utils.NamedList;

public class ME_RolePanel extends JContextualPanel {

	public static final ImageIcon ICON_ADD_ACTOR = new ImageIcon("resources/user_add_16x16.png");
	public static final ImageIcon ICON_REMOVE_ACTOR = new ImageIcon("resources/user_remove_16x16.png");
	public static final ImageIcon ICON_ADD_CAPACITY = new ImageIcon("resources/capacity_add_16x16.png");
	public static final ImageIcon ICON_REMOVE_CAPACITY = new ImageIcon("resources/capacity_remove_16x16.png");
	
	private static final long serialVersionUID = 1L;
	private RoleTableModel roleModel;
	private PddlModel model;
	private RoleCapacityTableModel capacityModel;
	private JTable capacityTable;

	public ME_RolePanel() {
		setLayout(new BorderLayout());

		IntegerEditor defaultIntEditor = new IntegerEditor(0, Integer.MAX_VALUE);

		// create the role table
		final JTable roleTable = new JTable(roleModel = new RoleTableModel());
		roleTable.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), "selectNextColumnCell");
		roleTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
		roleTable.setDefaultEditor(int.class, defaultIntEditor);

		setColumnInfo(roleTable, RoleTableModel.COL_NAME, 150, 50, Integer.MAX_VALUE, null);
		setColumnInfo(roleTable, RoleTableModel.COL_TEXT, 300, 50, Integer.MAX_VALUE, null);
		setColumnInfo(roleTable, RoleTableModel.COL_MAX_WORKLOAD, 90, 90, Integer.MAX_VALUE, defaultIntEditor);
		setColumnInfo(roleTable, RoleTableModel.COL_MAX_JOB, 80, 80, Integer.MAX_VALUE, defaultIntEditor);

		// create role command: add/remove
		JPanel roleTableCommand = new JPanel(new FlowLayout(FlowLayout.LEFT));
		JButton cmdAddRole = new JButton("Add new role", ICON_ADD_ACTOR);
		cmdAddRole.setMnemonic('A');
		cmdAddRole.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent e) {
				roleModel.createNewRole();
				roleTable.requestFocusInWindow();
				roleTable.changeSelection(roleModel.getRowCount() - 1, RoleTableModel.COL_NAME, false, false);
				roleTable.editCellAt(roleModel.getRowCount() - 1, RoleTableModel.COL_NAME);
			}

		});
		roleTableCommand.add(cmdAddRole);

		JButton cmdRemoveRole = new JButton("Remove roles", ICON_REMOVE_ACTOR);
		cmdRemoveRole.setMnemonic('R');
		cmdRemoveRole.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent e) {
				ListSelectionModel selectionModel = roleTable.getSelectionModel();
				roleModel.deleteRoles(selectionModel.getMinSelectionIndex(), selectionModel.getMaxSelectionIndex());
			}

		});
		roleTableCommand.add(cmdRemoveRole);

		JButton cmdAddCapacity = new JButton("Add capacity", ICON_ADD_CAPACITY);
		cmdAddCapacity.setMnemonic('c');
		cmdAddCapacity.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent e) {
				capacityModel.createNewCapacity();
				capacityTable.requestFocusInWindow();
				capacityTable.changeSelection(capacityModel.getRowCount() - 1, RoleCapacityTableModel.COL_GOAL, false, false);
				capacityTable.editCellAt(capacityModel.getRowCount() - 1, RoleCapacityTableModel.COL_GOAL);
			}

		});

		roleTableCommand.add(cmdAddCapacity);

		JButton cmdRemoveCapacity = new JButton("Remove capacity", ICON_REMOVE_CAPACITY);
		cmdRemoveCapacity.setMnemonic('e');
		cmdRemoveCapacity.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent e) {
				ListSelectionModel selectionModel = capacityTable.getSelectionModel();
				capacityModel.deleteCapacities(selectionModel.getMinSelectionIndex(), selectionModel.getMaxSelectionIndex());
			}

		});
		roleTableCommand.add(cmdRemoveCapacity);

		// create the role panel
		JPanel roleTablePanel = new JPanel(new BorderLayout());
		roleTablePanel.add(new JScrollPane(roleTable), BorderLayout.CENTER);
		roleTablePanel.add(roleTableCommand, BorderLayout.SOUTH);
		roleTablePanel.setBorder(new TitledBorder("Role List"));

		capacityTable = new JTable(capacityModel = new RoleCapacityTableModel(roleTable));
		capacityTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
		capacityTable.setDefaultEditor(int.class, defaultIntEditor);

		setColumnInfo(capacityTable, RoleCapacityTableModel.COL_GOAL, 150, 50, Integer.MAX_VALUE, null);
		setColumnInfo(capacityTable, RoleCapacityTableModel.COL_TEXT, 250, 50, Integer.MAX_VALUE, null);
		setColumnInfo(capacityTable, RoleCapacityTableModel.COL_SATISFACTION, 90, 90, Integer.MAX_VALUE, defaultIntEditor);
		setColumnInfo(capacityTable, RoleCapacityTableModel.COL_EFFORT, 90, 90, Integer.MAX_VALUE, defaultIntEditor);
		setColumnInfo(capacityTable, RoleCapacityTableModel.COL_TIME, 80, 80, Integer.MAX_VALUE, defaultIntEditor);

		// create the capacity panel
		JPanel capacityTablePanel = new JPanel(new BorderLayout());
		capacityTablePanel.add(new JScrollPane(capacityTable), BorderLayout.CENTER);
		capacityTablePanel.setBorder(new TitledBorder("Role Capacity"));

		// create the splitter panel
		JSplitPane spliter = new JSplitPane(JSplitPane.VERTICAL_SPLIT, roleTablePanel, capacityTablePanel);
		spliter.setResizeWeight(0.5);

		add(spliter, BorderLayout.CENTER);
	}

	private void setColumnInfo(final JTable table, int columnIndex, int preferredWidth, int minWidth, int maxWidth, TableCellEditor editor) {
		TableColumn column = table.getColumnModel().getColumn(columnIndex);
		if (editor != null)
			column.setCellEditor(editor);
		column.setMaxWidth(maxWidth);
		column.setMinWidth(minWidth);
		column.setPreferredWidth(preferredWidth);
	}

	@Override
	public void setContext(IAppContext context) {
		super.setContext(context);
		model = context.getService(PddlModel.class);
		roleModel.setModel(model);
		capacityModel.setModel(model);

		TableColumn column = capacityTable.getColumnModel().getColumn(RoleCapacityTableModel.COL_GOAL);
		JComboBox comboBox = new JComboBox(new NamedListComboBoxModel<PddlModel.Goal>(model.Goals));
		column.setCellEditor(new DefaultCellEditor(comboBox));
	}
}

class RoleTableModel extends AbstractTableModel {

	public static final int COL_NAME = 0;
	public static final int COL_TEXT = 1;
	public static final int COL_MAX_JOB = 3;
	public static final int COL_MAX_WORKLOAD = 2;

	private static final long serialVersionUID = 1L;

	private static String[] columnNames = new String[] { "Name", "Text", "Max Workload", "Max Job" };

	private NamedList<Actor> actors;
	private PddlModel model;

	@Override
	public int getColumnCount() {
		return columnNames.length;
	}

	public void deleteRoles(int minSelectionIndex, int maxSelectionIndex) {
		ArrayList<Actor> lst = new ArrayList<PddlModel.Actor>();
		for (int i = minSelectionIndex; i <= maxSelectionIndex; i++)
			lst.add(actors.get(i));
		for (PddlModel.Actor act : lst)
			actors.remove(act);
		fireTableDataChanged();
	}

	public void setModel(PddlModel model) {
		this.model = model;
		actors = model.Actors;
		fireTableDataChanged();
	}

	public void createNewRole() {
		model.addActor();
		fireTableDataChanged();
	}

	@Override
	public String getColumnName(int column) {
		return columnNames[column];
	}

	@Override
	public int getRowCount() {
		return actors != null ? actors.size() : 0;
	}

	@Override
	public Object getValueAt(int rowIndex, int columnIndex) {
		PddlModel.Actor act = actors.get(rowIndex);
		switch (columnIndex) {
		case 0: // Name
			return act.getName();
		case 1: // Text
			return act.getText();
		case COL_MAX_WORKLOAD: // Max Workload
			return act.getMaxWorkLoad();
		case COL_MAX_JOB: // Max Job
			return act.getMaxJobs();
		default:
			return null;
		}
	}

	@Override
	public void setValueAt(Object value, int rowIndex, int columnIndex) {
		if (rowIndex >= getRowCount() || rowIndex < 0)
			return;
		PddlModel.Actor act = actors.get(rowIndex);
		switch (columnIndex) {
		case 0: // Name
			act.setName((String) value);
			break;
		case 1: // Text
			act.setText((String) value);
			break;
		case COL_MAX_WORKLOAD: // Max Workload
			act.setMaxWorkLoad((Integer) value);
			break;
		case COL_MAX_JOB: // Max Job
			act.setMaxJobs((Integer) value);
			break;
		default:
			return;
		}
		fireTableCellUpdated(rowIndex, columnIndex);
	}

	@Override
	public boolean isCellEditable(int rowIndex, int columnIndex) {
		return true;
	}
}

class RoleCapacityTableModel extends AbstractTableModel implements ListSelectionListener {

	public static final int COL_GOAL = 0;
	public static final int COL_TEXT = 1;
	public static final int COL_SATISFACTION = COL_TEXT + 1;
	public static final int COL_EFFORT = COL_SATISFACTION + 1;
	public static final int COL_TIME = COL_EFFORT + 1;

	private static String[] columnNames = new String[] { "Goal", "Goal Text", "Satisfaction", "Effort", "Time" };

	private static final long serialVersionUID = 1L;
	private JTable roleTable;
	private PddlModel model;
	private Actor actActor;

	public RoleCapacityTableModel(JTable roleTable) {
		this.roleTable = roleTable;
		roleTable.getSelectionModel().addListSelectionListener(this);
	}

	public void deleteCapacities(int minSelectionIndex, int maxSelectionIndex) {
		if (actActor != null) {
			ArrayList<PddlModel.Capacity> lst = new ArrayList<PddlModel.Capacity>();
			for (int i = minSelectionIndex; i <= maxSelectionIndex; i++)
				lst.add(actActor.Capacity.get(i));
			for (PddlModel.Capacity act : lst)
				actActor.Capacity.remove(act);
			fireTableDataChanged();
		}
	}

	public void createNewCapacity() {
		if (actActor != null) {
			actActor.createCapacity();
			fireTableRowsInserted(actActor.Capacity.size() - 1, actActor.Capacity.size() - 1);
		}

	}

	public void setModel(PddlModel model) {
		this.model = model;
	}

	@Override
	public int getColumnCount() {
		return columnNames.length;
	}

	@Override
	public String getColumnName(int column) {
		return columnNames[column];
	}

	@Override
	public int getRowCount() {
		return actActor != null ? actActor.Capacity.size() : 0;
	}

	@Override
	public Object getValueAt(int rowIndex, int columnIndex) {
		Capacity capacity = actActor.Capacity.get(rowIndex);
		switch (columnIndex) {
		case COL_GOAL:
			return capacity.getGoal();
		case COL_TEXT:
			return capacity.getGoal() != null ? capacity.getGoal().getText() : null;
		case COL_SATISFACTION:
			return capacity.getSatisfaction();
		case COL_EFFORT:
			return capacity.getEffort();
		case COL_TIME:
			return capacity.getConsumeTime();
		}
		// fireTableCellUpdated(rowIndex, columnIndex);
		
		return null;
	}

	@Override
	public void setValueAt(Object value, int rowIndex, int columnIndex) {
		if (rowIndex >= getRowCount() || rowIndex < 0)
			return;
		Capacity capacity = actActor.Capacity.get(rowIndex);
		switch (columnIndex) {
		case COL_GOAL:
			capacity.setGoal((Goal) value);
			break;
		case COL_SATISFACTION:
			capacity.setSatisfaction((Integer) value);
			break;
		case COL_EFFORT:
			capacity.setEffort((Integer) value);
			break;
		case COL_TIME:
			capacity.setConsumeTime((Integer) value);
			break;
		}
		fireTableRowsUpdated(rowIndex, rowIndex);
	}

	@Override
	public boolean isCellEditable(int rowIndex, int columnIndex) {
		return columnIndex != COL_TEXT;
	}

	@Override
	public void valueChanged(ListSelectionEvent e) {
		int idx = roleTable.getSelectedRow();
		if (idx != -1) {
			actActor = model.Actors.get(idx);
		} else
			actActor = null;
		fireTableDataChanged();
	}

}